home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Deutsche Edition 1
/
Deutsche Edition 1.iso
/
amok
/
amok_lha
/
amok71.lha
/
Formula
/
Formula.mod
< prev
next >
Wrap
Text File
|
1993-08-15
|
25KB
|
888 lines
(*********************************************************************
:Program. Formula
:Contents. Module to evaluate numeric expressions like
:Contents. "sin(x)/epsilon*(time-7.2E3)"
:Author. Stefan Salewski
:Address. Stefan Salewski, Stolper Weg 3, D-2160 Stade
:Copyright. © 1992 by Stefan Salewski, see file Formula.doc
:Language. Oberon
:Translator. Amiga-Oberon-Compiler V2.14d
:Remark. Compile it with options 882 and 68030 set if You use
:Remark. it on an A3000. This speed up the calculations
:Remark. Be careful if You turn off stackcheck, this module
:Remark. uses recursion!
:Remark. No line in this file is longer than 70 characters!
:Remark. There is some death code in a few CASE-statments.
:Remark. I think this will not slow down the operations,
:Remark. but makes it more secure. Safety First!
:History. V1.0 11 MAY 1992
*********************************************************************)
MODULE Formula;
IMPORT
ASCII,
AVL,
Break, (* we don't really need it *)
LRC2:LongRealConversions2, (* from AMOK#58 *)
MATHLIB,
MathIEEEDoubBas, (* Floor() and Ceil() *)
(*MathIEEEDoubTrans, (* Pow() *) *)
NoGuru,
OberonLib, (* only StackCheck() *)
Random,
Requests,
Strings,
TF:TurboFiles; (* from AMOK#56, TurboFiles V1.1 is on this disk *)
CONST (* public Consts *)
Functions=31; (* How much math. functions like "sin" I know *)
FuncStrSize=8; (* >=LEN('DegToRad') *)
Values=2048; (* how much values like Pi, VelocityOfLight ... *)
Consts=512; (*how much const. numbers like '1.2E7' in formula*)
FormulaSize=1024; (* Formula can contain 1024 elements. element *)
(* means a number, an operator like "+", *)
(* a bracket or a basic function like sin() *)
TYPE
WriteProc*=PROCEDURE(str:ARRAY OF CHAR);
Formula*=RECORD
el:ARRAY FormulaSize OF INTEGER;
const:ARRAY Consts OF LONGREAL;
error*:INTEGER;
END;
CONST (* Errornumbers, = formula.error *)
NoError* = 0;
(* Errors from Evaluate() *)
Overflow* = 1;
DivisionByZero* = 2;
sqrtError* = 3;
lnError* = 4;
arcsinError* = 5;
arccosError* = 6;
artanhError* = 7;
facError* = 8;
rndError* = 9;
entierError* = 10;
powError* = 11;
(* Errors from Compile() *)
CompileError* = 12;
TYPE
String80=ARRAY 80 OF CHAR;
EText=ARRAY 13 OF String80;
CONST (* Messages for the Errornumbers above *)
ErrorText*=EText('No Error',
'Overflow: ABS(Result) >= MAX(LONGREAL)',
'Division by Zero',
'sqrt(x) only for x>=0',
'ln(x) only for x>0',
'arcsin(x) only for ABS(x)<=1',
'arccos(x) only for ABS(x)<=1',
'artanh(x) only for -1 < x < +1 ',
'fac(x) only for x = {0,1,2,...,170}',
'rnd(x) only for x = {1,2,3,...,MAX(INTEGER)}',
'entier(x) only for ABS(x)<MAX(LONGINT)',
'x^y: if x is negative, y must be an integer',
'Error in Compile()'
);
CONST (* private *)
MinStack=FormulaSize*10; (* recursion needs a large stack *)
(* I think stacksize is proportional to FormulaSize *)
FirstValue=0; (* Never change this *)
LastValue=Values-1;
FirstConst=LastValue+1;
LastConst=FirstConst+Consts-1;
FirstFunction=LastConst+1;
LastFunction=FirstFunction+Functions-1;
FirstOp=LastFunction+1;
Bra=FirstOp+0;
Ket=FirstOp+1;
Plus=FirstOp+2;
Minus=FirstOp+3;
Times=FirstOp+4;
Div=FirstOp+5;
Hi=FirstOp+6;
EOF=FirstOp+7;
LastOp=EOF;
TYPE
FuncStr=ARRAY FuncStrSize OF CHAR; (* 'sin', 'arctan', ... *)
FuncNames= ARRAY Functions OF FuncStr;
CONST
FunctionArray=
FuncNames('id','jump','entier','int','abs','sqr','sqrt',
'exp','ln','log','log10','log2','tentox','twotox',
'sin','arcsin','cos','arccos','tan','arctan',
'sinh','cosh','tanh','artanh',
'DegToRad','RadToDeg','RND','fac',
'ceil','floor','round');
F=FirstFunction;
id=F+0; jump=F+1; entier=F+2; int=F+3; abs=F+4; sqr=F+5; sqrt=F+6;
exp=F+7; ln=F+8; log=F+9; log10=F+10; log2=F+11; tentox=F+12;
twotox=F+13; sin=F+14; arcsin=F+15; cos=F+16; arccos=F+17;
tan=F+18; arctan=F+19; sinh=F+20; cosh=F+21; tanh=F+22;
artanh=F+23; DegToRad=F+24; RadToDeg=F+25; RND=F+26; fac=F+27;
ceil=F+28; floor=F+29; round=F+30;
AVLStringSize=SIZE(AVL.String);
Pi=3.141592653589793D;
MaxFac=170; (* fac(MaxFac+1) > MAX(LONGREAL) *)
Space=' ';
OoM="Not enougth Memory!";
ImpossibleError="I thought this is an impossible error!";
TYPE
Comment=String80;
ValueNodePtr=POINTER TO ValueNode;
ValueNode=RECORD (AVL.SNode)
index:INTEGER;
trash:BOOLEAN; (* IF trash, we don't save this value to disk *)
comment:Comment;
END;
FuncNodePtr=POINTER TO FuncNode;
FuncNode=RECORD (AVL.SNode)
index:INTEGER;
END;
ValueArray=ARRAY Values OF LONGREAL;
VAR
root:AVL.SRoot;
file:TF.File;
writeProc:WriteProc;
Fac:ARRAY (MaxFac+1) OF LONGREAL;
values:POINTER TO ValueArray;(* Allocate this array dynamically,*)
(*so we can use the small data model*)
ValueCounter:INTEGER;
PROCEDURE ImpErr;
BEGIN Requests.Assert(FALSE,ImpossibleError)
END ImpErr;
PROCEDURE InitFac;
VAR
i:INTEGER;
BEGIN
i:=1;
Fac[0]:=1;
REPEAT
Fac[i]:=i*Fac[i-1];
INC(i);
UNTIL i>MaxFac;
END InitFac;
PROCEDURE FAC(VAR x:LONGREAL):BOOLEAN;
VAR l:LONGINT;
BEGIN
IF (x<0) OR (x>MaxFac) THEN RETURN FALSE END;
l:=ENTIER(x);
IF x#l THEN
RETURN FALSE
ELSE
x:=Fac[l];
RETURN TRUE
END;
END FAC;
PROCEDURE RN(VAR x:LONGREAL):BOOLEAN;
VAR i:INTEGER;
BEGIN
IF (x<1) OR (x>=MAX(INTEGER)) THEN RETURN FALSE END;
i:=SHORT(ENTIER(x));
IF x#i THEN
RETURN FALSE
ELSE
x:=Random.RND(i);
RETURN TRUE;
END;
END RN;
PROCEDURE FindValue*(name:ARRAY OF CHAR; VAR x:LONGREAL;
VAR comment:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
snodePtr:AVL.SNodePtr;
str:AVL.String;
BEGIN
x:=0;
comment:='';
COPY(name,str);
snodePtr:=AVL.SFind(root,str);
IF snodePtr=NIL THEN RETURN FALSE END;
IF snodePtr IS FuncNode THEN RETURN FALSE END;
IF snodePtr IS ValueNode THEN
COPY(snodePtr(ValueNode).comment,comment);
x:=values^[snodePtr(ValueNode).index];
RETURN TRUE;
ELSE
ImpErr
END;
END FindValue;
PROCEDURE GetIndex*(name:ARRAY OF CHAR;VAR index:INTEGER):BOOLEAN;
(* $CopyArrays- *)
VAR
snodePtr:AVL.SNodePtr;
str:AVL.String;
BEGIN
index:=-1;
COPY(name,str);
snodePtr:=AVL.SFind(root,str);
IF snodePtr=NIL THEN RETURN FALSE END;
IF snodePtr IS FuncNode THEN RETURN FALSE END;
IF snodePtr IS ValueNode THEN
index:=snodePtr(ValueNode).index;
RETURN TRUE;
ELSE
ImpErr
END;
END GetIndex;
PROCEDURE ChangeValue*(index:INTEGER; value:LONGREAL);
BEGIN
(* only for 0 <= index < MaxValues *)
values^[index]:=value
END ChangeValue;
PROCEDURE Split*(VAR str1,str2:ARRAY OF CHAR; c:CHAR):BOOLEAN;
(* splits str1 at position determined by c in str1 and str2 *)
(* If str1="" then str1:="" and str2:="" *)
(* IF c is the last Char in str1 then str1:=str1-c and str2:="" *)
(* IF c is the first Char in str1 then str1:="" and str2:=str1-c *)
(* RETURNS TRUE if (c is in str1) *)
VAR
i:INTEGER;
j,l:INTEGER;
found:BOOLEAN;
BEGIN
i:=0; j:=0; found:=FALSE;
l:=Strings.Length(str1);
WHILE (i<l) AND (str1[i]#c) DO INC(i) END;
IF i<l THEN str1[i]:=0X; found:=TRUE END;
INC(i);
WHILE (i<l) AND (j<LEN(str2)) DO
str2[j]:=str1[i];
INC(i); INC(j);
END;
IF j<LEN(str2) THEN str2[j]:=0X END;
RETURN found
END Split;
PROCEDURE DeleteSpaces*(VAR str:ARRAY OF CHAR);
(* removes all spaces on the left and right side of str *)
VAR
i:INTEGER;
BEGIN
i:=0;
WHILE (i<LEN(str)) AND (str[i]=Space) DO INC(i) END;
IF i>0 THEN Strings.Delete(str,0,i) END;
i:=Strings.Length(str);
WHILE (i>0) AND (str[i-1]=Space) DO DEC(i); str[i]:=0X END;
END DeleteSpaces;
PROCEDURE Divide*(VAR input,name,expression,comment:ARRAY OF CHAR);
(* input= "pi = 2*arcsin(1) ; ~3.14"
==> name= "pi"
==> expression= "2*arcsin(1)"
==> comment= "~3.14"
*)
BEGIN
IF Split(input,comment,';') THEN DeleteSpaces(comment) END;
IF Split(input,expression,'=') THEN
DeleteSpaces(expression);
DeleteSpaces(input);
COPY(input,name)
ELSE
name:='';
DeleteSpaces(input);
COPY(input,expression);
END;
END Divide;
PROCEDURE NameOK(name:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
i:INTEGER;
BEGIN
(*IF Strings.Length(name)>AVLStringSize THEN RETURN FALSE END;*)
IF (name[0]>='0') AND (name[0]<='9') THEN RETURN FALSE END;
i:=0;
LOOP
IF i=LEN(name) THEN EXIT END;
CASE name[i] OF
0X:EXIT|
01X..ASCII.us,ASCII.del,ASCII.csi,Space,'+','-','*','/','^',
'(','[','{','}',']',')',':','=','.':
RETURN FALSE
ELSE END;
INC(i);
END;
RETURN (i>0) AND (i<AVLStringSize)
END NameOK;
PROCEDURE DefineValue*(name:ARRAY OF CHAR;value:LONGREAL;
trash:BOOLEAN;comment:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
vPtr:ValueNodePtr;
el:AVL.SNodePtr;
avlName:AVL.String;
BEGIN
IF NOT NameOK(name) THEN RETURN FALSE END;
COPY(name,avlName);
el:=AVL.SFind(root,avlName);
IF (el#NIL) THEN
IF (el IS ValueNode) THEN
COPY (comment,el(ValueNode).comment);
el(ValueNode).trash:=trash;
values^[el(ValueNode).index]:=value;
RETURN TRUE
ELSE
RETURN FALSE
END;
END;
IF ValueCounter=Values THEN RETURN FALSE END;
NEW(vPtr);
Requests.Assert(vPtr#NIL,OoM);
vPtr.name:=avlName;
COPY(comment,vPtr.comment);
vPtr.index:=ValueCounter;
vPtr.trash:=trash;
IF AVL.SAdd(root,vPtr) THEN
values^[ValueCounter]:=value;
INC(ValueCounter);
RETURN TRUE
ELSE
RETURN FALSE
END;
END DefineValue;
PROCEDURE LoadValues*(filename:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
CONST
LineLen=256;
VAR
ok:BOOLEAN;
file:TF.File;
line:ARRAY LineLen OF CHAR;
name:AVL.String;
value:String80;
com:Comment;
x:LONGREAL;
len:INTEGER;
BEGIN
(* Don't delete old values, only add new or overwrite old values *)
IF TF.Open(file,filename,1024,TF.oldFile) THEN
LOOP
len:=TF.ReadString(file,line);
ok:=len<LineLen;
IF NOT ok THEN EXIT END;
IF len>0 THEN
Divide(line,name,value,com);
IF (name#'') OR (value#'') THEN
ok:=NameOK(name) & (value#'') & LRC2.StringToReal(value,x)
AND DefineValue(name,x,FALSE,com);
IF NOT ok THEN EXIT END;
END;
END;
IF (file.res#TF.done) THEN EXIT END;
END;
ok:=ok AND (file.res=TF.endOfFile);
RETURN TF.Close(file) AND ok;
ELSE
RETURN FALSE
END;
END LoadValues;
PROCEDURE * SaveValue(el:AVL.NodePtr);
CONST GS=14;
VAR
i:INTEGER;
str:ARRAY (GS+7) OF CHAR;
BEGIN
IF (el IS ValueNode) AND NOT el(ValueNode).trash THEN
WITH el:ValueNode DO
IF TF.WriteString(file,el.name) THEN END;
i:=Strings.Length(el.name);
WHILE i<20 DO
IF TF.WriteChar(file,Space) THEN END;
INC(i)
END;
IF TF.WriteString(file,' = ') THEN END;
IF NOT LRC2.RealToString(values^[el.index],str,GS,GS,TRUE,TRUE)
THEN ImpErr END;
IF TF.WriteString(file,str) THEN END;
IF el.comment#'' THEN
IF TF.WriteString(file,' ; ') THEN END;
IF TF.WriteString(file,el.comment) THEN END;
END;
END;
IF TF.WriteLn(file) THEN END;
END;
END SaveValue;
PROCEDURE SaveValues*(filename:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
ok:BOOLEAN;
BEGIN
IF filename="" THEN RETURN FALSE END;
IF TF.Open(file,filename,1024,TF.newFile) THEN
AVL.DoForward(root,SaveValue);
ok:=file.res=TF.done;
RETURN TF.Close(file) AND ok;
ELSE RETURN FALSE END;
END SaveValues;
PROCEDURE * WriteValue(el:AVL.NodePtr);
CONST GS=14;
VAR
str:ARRAY 256 OF CHAR; (* 1 Line *)
s:ARRAY (GS+7) OF CHAR;
i:INTEGER;
BEGIN
IF el IS ValueNode THEN
WITH el:ValueNode DO
COPY(el.name,str);
i:=Strings.Length(str);
WHILE i<20 DO
str[i]:=Space;
INC(i);
str[i]:=0X;
END;
IF NOT LRC2.RealToString(values^[el.index],s,GS,GS,TRUE,TRUE)
THEN ImpErr END;
Strings.Append(str,' = ');
Strings.Append(str,s);
IF el.comment#'' THEN
Strings.Append(str,' ; ');
Strings.Append(str,el.comment);
END;
END;
writeProc(str);
END;
END WriteValue;
PROCEDURE WriteValues*(p:WriteProc);
BEGIN
IF p#NIL THEN
writeProc:=p; (* I don't like this way, using a global *)
(* procedurevariable, but this is the only way *)
AVL.DoForward(root,WriteValue);
END;
END WriteValues;
PROCEDURE * WriteFunction(el:AVL.NodePtr);
VAR
str:ARRAY 16 OF CHAR;
BEGIN
IF el IS FuncNode THEN
COPY(el(FuncNode).name,str);
Strings.Append(str,'()');
writeProc(str);
END;
END WriteFunction;
PROCEDURE WriteFunctions*(p:WriteProc);
BEGIN
IF p#NIL THEN
writeProc:=p;
AVL.DoForward(root,WriteFunction);
END;
END WriteFunctions;
PROCEDURE AddFunctionsToAVL;
VAR
i:INTEGER;
fnPtr:FuncNodePtr;
BEGIN
i:=0;
WHILE i<Functions DO
NEW(fnPtr);
Requests.Assert(fnPtr#NIL,OoM);
COPY(FunctionArray[i],fnPtr.name);
fnPtr.index:=FirstFunction+i;
IF NOT AVL.SAdd(root,fnPtr) THEN ImpErr END;
INC(i);
END;
END AddFunctionsToAVL;
PROCEDURE RemoveValue*(name:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
el:AVL.NodePtr;
avlName:AVL.String;
BEGIN
COPY(name,avlName);
el:=AVL.SFind(root,avlName);
IF (el#NIL) THEN
IF (el IS ValueNode) THEN
values^[el(ValueNode).index]:=MAX(LONGREAL); (* NOT valid *)
IF AVL.Remove(root,el) THEN
DISPOSE(el);
RETURN TRUE
ELSE
RETURN FALSE
END;
ELSE
RETURN FALSE
END;
ELSE
RETURN FALSE
END;
END RemoveValue;
PROCEDURE DisposeAllValues*;
BEGIN
AVL.Dispose(root);
ValueCounter:=0;
AddFunctionsToAVL;
END DisposeAllValues;
PROCEDURE Compile*(str:ARRAY OF CHAR;VAR formula:Formula):BOOLEAN;
(* $CopyArrays- *)
PROCEDURE Scan(str:ARRAY OF CHAR):BOOLEAN;
(* $CopyArrays- *)
VAR
strPos:INTEGER;
bufPos,fPos,constCount:INTEGER;
x:LONGREAL;
buf:AVL.String;
overflow:BOOLEAN; (* only TRUE if str contains nonsens *)
PROCEDURE GetIt;
BEGIN
IF bufPos<(AVLStringSize-1) THEN
buf[bufPos]:=str[strPos];
INC(bufPos);
buf[bufPos]:=0X;
ELSE
overflow:=TRUE
END;
INC(strPos);
END GetIt;
PROCEDURE Put(v:INTEGER);
BEGIN
IF fPos<FormulaSize THEN (* ELSE overflow, we check it later *)
formula.el[fPos]:=v;
INC(fPos);
END;
END Put;
PROCEDURE Find(name:AVL.String):BOOLEAN;
(* $CopyArrays- *)
VAR
snodePtr:AVL.SNodePtr;
BEGIN
snodePtr:=AVL.SFind(root,name);
IF snodePtr=NIL THEN RETURN FALSE END;
IF snodePtr IS FuncNode THEN
Put(snodePtr(FuncNode).index);
ELSIF snodePtr IS ValueNode THEN
Put(snodePtr(ValueNode).index)
ELSE
ImpErr
END;
RETURN TRUE
END Find;
PROCEDURE ScanReal():BOOLEAN;
BEGIN
bufPos:=0;
WHILE (str[strPos]>='0') AND (str[strPos]<='9') DO GetIt END;
IF str[strPos]='.' THEN
GetIt;
WHILE (str[strPos]>='0') AND (str[strPos]<='9') DO GetIt END;
END;
IF str[strPos]='E' THEN
GetIt;
IF (str[strPos]='+') OR (str[strPos]='-') THEN GetIt END;
WHILE (str[strPos]>='0') AND (str[strPos]<='9') DO GetIt END;
END;
RETURN LRC2.StringToReal(buf,x);
END ScanReal;
PROCEDURE ScanName;
BEGIN
bufPos:=0;
LOOP
CASE str[strPos] OF
0X,'(','[','{','}',']',')','+','-','*','/','^':
EXIT;
ELSE
GetIt;
END;
END;
END ScanName;
BEGIN
overflow:=FALSE;
strPos:=0; fPos:=0;
constCount:=0;
LOOP
CASE str[strPos] OF
0X:EXIT|
'(','[','{':Put(Bra);INC(strPos)|
')',']','}':Put(Ket);INC(strPos)|
'+':Put(Plus);INC(strPos)|
'-':Put(Minus);INC(strPos)|
'*':Put(Times);INC(strPos)|
'/':Put(Div);INC(strPos)|
'^':Put(Hi);INC(strPos)|
'0'..'9','.': IF NOT ScanReal() OR (constCount=Consts) THEN
RETURN FALSE
ELSE
formula.const[constCount]:=x;
Put(FirstConst+constCount);
INC(constCount)
END;
ELSE
ScanName;
IF NOT Find(buf) THEN RETURN FALSE END;
END
END;
Put(EOF);
RETURN (NOT overflow) AND (fPos<FormulaSize);
END Scan;
PROCEDURE SyntaxOK(VAR f:Formula):BOOLEAN;
VAR
o,i:INTEGER;
BEGIN
o:=0;
i:=0;
CASE f.el[i] OF
FirstValue..LastFunction,Plus,Minus,Bra(*,EOF*):
ELSE RETURN FALSE END;
LOOP
INC(i);
CASE f.el[i-1] OF
EOF:EXIT|
FirstValue..LastConst:CASE f.el[i] OF Ket,Plus..Hi,EOF:
ELSE RETURN FALSE END|
FirstFunction..LastFunction:
CASE f.el[i] OF Bra: ELSE RETURN FALSE END|
Bra:INC(o);
CASE f.el[i] OF Ket,EOF,Times..Hi:RETURN FALSE ELSE END|
Ket:DEC(o);
CASE f.el[i] OF Plus..Hi,Ket,EOF: ELSE RETURN FALSE END|
Plus..Hi:CASE f.el[i] OF FirstValue..LastFunction,Bra:
ELSE RETURN FALSE END;
ELSE RETURN FALSE END;
END;
RETURN o=0;
END SyntaxOK;
BEGIN
IF Scan(str) AND SyntaxOK(formula) THEN
formula.error:=NoError;
RETURN TRUE;
ELSE
formula.error:=CompileError;
RETURN FALSE
END;
END Compile;
PROCEDURE Evaluate*(VAR formula:Formula; VAR res:LONGREAL):BOOLEAN;
VAR
pos:INTEGER;
op:INTEGER;
PROCEDURE ^ Sum():LONGREAL;
PROCEDURE Next():LONGREAL;
(* Returns current number and sets op to next operator *)
(* After a call of Next() formula.el[pos] is the next number *)
VAR x:LONGREAL;
c:INTEGER;
num:INTEGER;
BEGIN
CASE formula.el[pos] OF
FirstFunction..LastFunction:c:=formula.el[pos];INC(pos)
ELSE
c:=id;
END;
num:=formula.el[pos];
IF num=Bra THEN
INC(pos);
x:=Sum()
ELSIF num<=LastValue THEN
x:=values^[num];INC(pos);
ELSIF num<=LastConst THEN
x:=formula.const[num-FirstConst];INC(pos);
ELSE
ImpErr
END;
CASE formula.el[pos] OF
FirstOp..LastOp:op:=formula.el[pos]
ELSE
ImpErr
END;
INC(pos);
CASE c OF (* simpel and often used functions first *)
id:RETURN x|
jump:IF x>0 THEN RETURN 1 ELSE RETURN 0 END|
entier:IF ABS(x)>MAX(LONGINT) THEN formula.error:=entierError
ELSE RETURN ENTIER(x) END|
int:RETURN MATHLIB.INT(x)|
ceil:RETURN MathIEEEDoubBas.Ceil(x)|
floor:RETURN MathIEEEDoubBas.Floor(x)|
round:RETURN MathIEEEDoubBas.Floor(x+0.5D)|
abs:RETURN ABS(x)|
sqr:x:=MATHLIB.SQR(x)|
sqrt:IF x<0 THEN formula.error:=sqrtError
ELSE RETURN MATHLIB.SQRT(x) END|
exp:x:=MATHLIB.ETOX(x)|
ln:IF x>0 THEN RETURN MATHLIB.LOGN(x)
ELSE formula.error:=lnError END|
log,log10:IF x>0 THEN RETURN MATHLIB.LOG10(x)
ELSE formula.error:=lnError END|
log2:IF x>0 THEN RETURN MATHLIB.LOG2(x)
ELSE formula.error:=lnError END|
tentox:x:=MATHLIB.TENTOX(x)|
twotox:x:=MATHLIB.TWOTOX(x)|
sin:RETURN MATHLIB.SIN(x)|
arcsin:IF ABS(x)>1 THEN formula.error:=arcsinError
ELSE RETURN MATHLIB.ASIN(x) END|
cos:RETURN MATHLIB.COS(x)|
arccos:IF ABS(x)>1 THEN formula.error:=arccosError
ELSE RETURN MATHLIB.ACOS(x) END|
tan:x:=MATHLIB.TAN(x)|
arctan:RETURN MATHLIB.ATAN(x)|
sinh:RETURN MATHLIB.SINH(x)|
cosh:RETURN MATHLIB.COSH(x)|
tanh:RETURN MATHLIB.TANH(x)|
artanh:IF ABS(x)<1 THEN RETURN MATHLIB.ATANH(x)
ELSE formula.error:=artanhError END|
DegToRad:RETURN x*(Pi/180)|
RadToDeg: x:=x*(180/Pi)|
RND: IF RN(x) THEN RETURN x ELSE formula.error:=rndError END|
fac: IF FAC(x) THEN RETURN x ELSE formula.error:=facError END|
END;
IF ABS(x)=MAX(LONGREAL) THEN formula.error:=Overflow END;
RETURN x;
END Next;
PROCEDURE Pot():LONGREAL;
(* I don't trust MathIEEEDoubTrans.Pow(exp,base).
There can be problems if base is negative. Under OS2.0 it may
work, but what is under KS1.2, 1.3 or 2.x. So I use LOGN(x).
This should be safe if x is greater than zero.
(base^exp=Pow(exp,base)=ETOX(exp*LOGN(base))
*)
VAR
base:LONGREAL;
exp:LONGREAL;
l:LONGINT;
BEGIN
base:=Next();
LOOP
CASE op OF
Hi:exp:=Next();
IF (base>0) THEN (* no problem *)
base:=MATHLIB.ETOX(exp*MATHLIB.LOGN(base))
ELSIF base=0 THEN (*base:=0*)
ELSIF ABS(exp)<MAX(LONGINT) THEN
l:=ENTIER(exp);
IF l=exp THEN
base:=MATHLIB.ETOX(exp*MATHLIB.LOGN(ABS(base)));
IF ODD(l) THEN base:=-base END
ELSE
formula.error:=powError
END
ELSE
formula.error:=powError
END|
Plus,Minus,Ket,EOF,Times,Div:EXIT
(*Operators for Prod() or Sum() *)
ELSE
ImpErr
END;
IF ABS(base)=MAX(LONGREAL) THEN formula.error:=Overflow END;
END;
RETURN base
END Pot;
PROCEDURE Prod():LONGREAL;
VAR
prod:LONGREAL;
h:LONGREAL;
BEGIN
prod:=Pot();
LOOP
CASE op OF
Times:prod:=prod*Pot()|
Div:h:=Pot();
IF h#0 THEN prod:=prod/h ELSE formula.error:=DivisionByZero END|
Plus,Minus,Ket,EOF:EXIT (* operators for Sum() *)
ELSE
ImpErr
END;
IF ABS(prod)=MAX(LONGREAL) THEN formula.error:=Overflow END;
END;
RETURN prod
END Prod;
PROCEDURE Sum():LONGREAL;
(* Every "(" calls Sum(), so turn on StachCheck here! *)
(* $StackChk+ *)
VAR
sum:LONGREAL;
BEGIN
sum:=0;
op:=formula.el[pos];
CASE op OF
EOF:|
Plus,Minus:INC(pos)
ELSE
op:=Plus
END;
LOOP
CASE op OF
Plus:sum:=sum+Prod()|
Minus:sum:=sum-Prod()|
Ket,EOF:EXIT
ELSE
ImpErr
END;
IF ABS(sum)=MAX(LONGREAL) THEN formula.error:=Overflow END;
END;
RETURN sum;
(* $StackChk= *)
END Sum;
BEGIN
pos:=0;
formula.error:=NoError;
res:=Sum();
RETURN formula.error=NoError;
END Evaluate;
BEGIN
OberonLib.StackChk(MinStack);
(* IF our stack is too small then abort. *)
(* This Check is no guarantee that we have *)
(* enougth stack all the time. *)
NEW(values);
Requests.Assert(values#NIL,OoM);
ValueCounter:=0;
InitFac;
AVL.SInit(root);
AddFunctionsToAVL;
END Formula.